/*
 * pack.c
 *
 *  Created on: Sep 6, 2020
 *      Author: daniel
 */

#include "pack.h"
#include "chachapoly.h"
#include "stm32l0xx_hal.h"
#include <time.h>
#include <string.h>
#include "ws_data.h"

uint8_t		packCounter = 0;

#define NODE_ID		0x01
#define HOST_ID		0x00

uint8_t PayloadPack(const WS_DATA* data, uint8_t* buffer, uint8_t buffersize);


void payload_aead_encrypt(uint8_t* buffer, uint8_t bufsize, uint8_t* random_p, uint8_t* tag);
bool payload_aead_decrypt(uint8_t* buffer, uint8_t bufsize, uint8_t* random_p, uint8_t* tag);

// LoRA Payload编码格式
/*
 * byte		说明
 * 1:		发送节点编号（主机永远是0。发送节点永远是自己的编号）
 * 2：		接收节点编号
 * 3：		命令（0：数据；   1：请求Firmware；   2：数据ACK；   3：Firmware data）；   4：请求配置数据；    5：配置数据响应
 * 4：		序号（0～255)
 * 5-(5+8):	随机数（8bytes）
 * 14-（14+16）：tag（poly1305的计算结果）
 * 27-:		Payload数据
 *
 * Payload说明：
 * 数据和数据的ACK全部使用chacha20-poly1305加密（实际也是同时实现数据校验）
 *
 * 数据的ACK Payload：
 * 1：0，None； 1，有升级数据； 2，有配置数据
 *
 * Firmware请求Payload格式：
 * 1，2：	16bit的位置序号。（每个包64字节），支持断点续传，但是只能按顺序传。
 *
 * Firmware数据Payload格式：
 * 不携带位置序号（或者称为，不响应位置序号）。对于请求的响应，只对“包头的序号”
 */

// 数据编码，根据传入的mask进行编码
// 编码顺序固定，根据mask的顺序，由低位到高位类型的数据
// 编码后，再加密
// 返回最后的数据长度（包含包头和CRC16)
uint8_t packData(const WS_DATA* data, uint8_t* buffer, uint8_t buffersize, uint8_t* no)
{
	uint8_t		packSize = 0;
	uint8_t		payloadSize = 0;
	uint8_t*	random_pos;
	uint8_t*	tag_pos;
	uint8_t*	payload_pos;

	// send id
	buffer[packSize++] = NODE_ID;

	// receive id
	buffer[packSize++] = HOST_ID;

	// random (8bytes)
	random_pos = buffer + packSize;
	packSize += 8;

	// poly1305 (16bytes)
	tag_pos = buffer + packSize;
	packSize += POLY1305_BLOCK_SIZE;

	payload_pos = buffer + packSize;
	// cmd
	buffer[packSize++] = CMD_DATA;

	// pack no
	*no = packCounter++;
	buffer[packSize++] = *no;

	// payload encode
	payloadSize = PayloadPack(data, buffer + packSize, buffersize - packSize);

	// payload encrypt
	// 流式加密，长度一致（连同cmd和no）
	payload_aead_encrypt(payload_pos, payloadSize + 2, random_pos, tag_pos);

	packSize += payloadSize;

	return packSize;
}


uint8_t PayloadPack(const WS_DATA* data, uint8_t* buffer, uint8_t buffersize)
{
	uint8_t		pos = 0;

	// Mask
	buffer[pos++] = data->dataMask & 0xFF;

	buffer[pos++] = (data->temperature >> 8) & 0xFF;
	buffer[pos++] = data->temperature & 0xFF;

	buffer[pos++] = (data->humidity) >> 8 & 0xFF;
	buffer[pos++] = data->humidity & 0xFF;

	buffer[pos++] = (data->wind_dir >> 8) & 0xFF;
	buffer[pos++] = data->wind_dir & 0xFF;

	buffer[pos++] = (data->wind_speed >> 8) & 0xFF;
	buffer[pos++] = data->wind_speed & 0xFF;

	buffer[pos++] = (data->pressure >> 8) & 0xFF;
	buffer[pos++] = data->pressure & 0xFF;

	buffer[pos++] = (data->solar_radiation >> 8) & 0xFF;
	buffer[pos++] = data->solar_radiation & 0xFF;

	buffer[pos++] = (data->illumination >> 24) & 0xFF;
	buffer[pos++] = (data->illumination >> 16) & 0xFF;
	buffer[pos++] = (data->illumination >> 8) & 0xFF;
	buffer[pos++] = data->illumination & 0xFF;

	buffer[pos++] = (data->uv_index >> 8) & 0xFF;
	buffer[pos++] = data->uv_index & 0xFF;

	buffer[pos++] = (data->uva_value >> 8) & 0xFF;
	buffer[pos++] = data->uva_value & 0xFF;

	buffer[pos++] = (data->uvb_value >> 8) & 0xFF;
	buffer[pos++] = data->uvb_value & 0xFF;

	buffer[pos++] = (data->battery_voltage >> 8) & 0xFF;
	buffer[pos++] = data->battery_voltage & 0xFF;

	buffer[pos++] = (data->battery_current >> 8) & 0xFF;
	buffer[pos++] = data->battery_current & 0xFF;

	//#define DATA_RAIN
	buffer[pos++] = (data->rain >> 24) & 0xFF;
	buffer[pos++] = (data->rain >> 16) & 0xFF;
	buffer[pos++] = (data->rain >> 8) & 0xFF;
	buffer[pos++] = data->rain & 0xFF;

	//#define DATA_LIGHTNING	(0x1000)
	buffer[pos++] = (data->lightning_dist >> 8) & 0xFF;
	buffer[pos++] = data->lightning_dist & 0xFF;

	if (data->dataMask & DATA_FIRMWARE)
	{
		buffer[pos++] = data->firmware;
	}

	return pos;
}

/*
 * pos = x		：每64bytes一个包
 */
uint8_t packFirmwareGet(uint16_t pos, uint8_t* buffer, uint8_t buffersize, uint8_t* no)
{
	uint8_t		packSize = 0;
	uint8_t*	random_pos;
	uint8_t*	tag_pos;
	uint8_t*	payload_pos;

	// send id
	buffer[packSize++] = NODE_ID;

	// receive id
	buffer[packSize++] = HOST_ID;

	// random (8bytes)
	random_pos = buffer + packSize;
	packSize += 8;

	// poly1305 (16bytes)
	tag_pos = buffer + packSize;
	packSize += POLY1305_BLOCK_SIZE;

	payload_pos = buffer + packSize;
	// cmd
	buffer[packSize++] = CMD_FIRMWARE_GET;

	// pack no
	*no = packCounter++;
	buffer[packSize++] = *no;

	// payload encode
	buffer[packSize++] = (pos >> 8) & 0xFF;
	buffer[packSize++] = pos & 0xFF;

	// payload encrypt
	// 流式加密，长度一致（连同cmd和no）
	payload_aead_encrypt(payload_pos, 2 + 2, random_pos, tag_pos);

	return packSize;
}

uint8_t PayloadDataAck(uint8_t* buffer, uint8_t bufferSize)
{
	WS_DATA_ACK		ack;

	ack.data = buffer[0];

	if (ack.data == FIRMWARE_INFO_NEW) {
		// 有固件信息
		ack.firmware_info.firmwareVer = (buffer[1] << 24) + (buffer[2] << 16) + (buffer[3] << 8) + buffer[4];
		ack.firmware_info.firmwareSize = (buffer[5] << 8) + buffer[6];
		memcpy(ack.firmware_info.tag, buffer + 7, 16);
	}

	ws_data_ack(&ack);

	return bufferSize;
}

uint8_t PayloadFirmwareData(uint8_t* buffer, uint8_t bufferSize)
{
	if (bufferSize > 64) {
		return 0;
	}

	ws_firmware_data(buffer, bufferSize);
	return bufferSize;
}

uint8_t unpackData(uint8_t* buffer, uint8_t bufferSize, uint8_t no)
{
	uint8_t		payload_size;
	uint8_t*	payload_pos;
	uint8_t*	random_pos;
	uint8_t*	tag_pos;
	bool		decrypt_ok;

	// 至少 1 + 1 + 8 + 16 + 2字节
	if (bufferSize <= ( 1 + 1 + 8 + 16 + 2))
	{
		return 0;
	}

	// 判断是否主机发出，是否本机接收
	if ( (buffer[0] == NODE_ID) || (buffer[1] != NODE_ID) )
	{
		return 0;
	}

	random_pos = buffer + 2;
	tag_pos = buffer + 2 + 8;
	payload_pos = buffer + 2 + 8 + 16;
	payload_size = bufferSize - 2 - 8 - 16;
	decrypt_ok = payload_aead_decrypt(payload_pos, payload_size, random_pos, tag_pos);

	if (!decrypt_ok)
	{
		return 0;
	}

	// 判断回应序号是否正确
	if (no != payload_pos[1]) {
		return 0;
	}

	switch (payload_pos[0]) {
	case CMD_DATA_ACK:
		payload_size = PayloadDataAck(payload_pos + 2, payload_size - 2);
		break;

	case CMD_FIRMWARE_DATA:
		payload_size = PayloadFirmwareData(payload_pos + 2, payload_size - 2);
		break;

	default:
		payload_size = 0;
		break;
	}

	// 返回实际解码获得的数据长度
	return payload_size;
}

static struct chachapoly_ctx ctx;

const unsigned char key[32] = {
		0xf9, 0xd7, 0xde, 0xe9, 0xa9, 0xcb, 0xbd, 0xec, 0xbf, 0xf8, 0x8a, 0xa7, 0x78, 0xb5, 0x99, 0xb2,
		0x8c, 0x1a, 0xdf, 0xd6, 0x26, 0x28, 0x92, 0x3a, 0xae, 0xe4, 0x76, 0xc5, 0xec, 0x2f, 0x38, 0x1c
};

const unsigned char ad[12] = {
		0xce, 0x86, 0x81, 0x95, 0x3d, 0x44, 0x63, 0x51, 0xac, 0xb8, 0x45, 0x39
};

void payload_aead_init()
{
	chachapoly_init(&ctx, key, 256);
}

/*
 * buffer：明文数据，输入。同时输出加密数据
 * bufsize：数据长度
 * random：随机数输出（8bytes）
 * tag：auth tag输出（16bytes）
 */
void payload_aead_encrypt(uint8_t* buffer, uint8_t bufsize, uint8_t* random_p, uint8_t* tag)
{
	uint32_t	r;
	uint32_t	seed = ws_GetCount();


	// 生成随机数
	srandom(seed);
	r = (uint32_t)random();
	random_p[0] = r & 0xFF;
	random_p[1] = (r >> 8) & 0xFF;
	random_p[2] = (r >> 16) & 0xFF;

	r = (uint32_t)random();
	random_p[3] = r & 0xFF;
	random_p[4] = (r >> 8) & 0xFF;
	random_p[5] = (r >> 16) & 0xFF;

	r = (uint32_t)random();
	random_p[6] = r & 0xFF;
	random_p[7] = (r >> 8) & 0xFF;

	// 加密
	chachapoly_crypt(&ctx, random_p, ad, 12, buffer, bufsize, buffer, tag, 16, 1);
}

bool payload_aead_decrypt(uint8_t* buffer, uint8_t bufsize, uint8_t* random_p, uint8_t* tag)
{
	int	ret = 0;

	ret = chachapoly_crypt(&ctx, random_p, ad, 12, buffer, bufsize, buffer, tag, 16, 0);

	return (ret == 0);
}
